﻿using System;
using System.Activities;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Umbrella.Whs.Service
{
    public static class UmbrellaWorkflowInvoker
    {
        private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(typeof(UmbrellaWorkflowInvoker));

        public static IDictionary<string, object> Invoke<T>(IDictionary<string, object> inputParams) where T : Activity, new()
        {
            if (inputParams == null)
                throw new ArgumentNullException("inputParams");

            return Invoke(new T(), inputParams);
        }

        public static IDictionary<string, object> Invoke<T>() where T : Activity, new()
        {
            return Invoke(new T(), null);
        }

        public static IDictionary<string, object> Invoke<T>(T wfl) where T : Activity
        {
            if (wfl == null)
                throw new ArgumentNullException("wfl");

            return Invoke(wfl, null);
        }

        public static IDictionary<string, object> Invoke<T>(T wfl, IDictionary<string, object> inputParams) where T : Activity
        {
            if (wfl == null)
                throw new ArgumentNullException("wfl");

            if (_log.IsDebugEnabled)
                _log.Debug("Starting workflow...");

            // create worflow
            WorkflowApplication app;

            if (inputParams == null)
            {
                app = new WorkflowApplication(wfl);
            }
            else
            {
                app = new WorkflowApplication(wfl, inputParams);
            }
            
            // add extensions
            app.Extensions.Add(WorkflowStatusManager.Instance);

            // prepare output
            var wait = new ManualResetEvent(false);
            IDictionary<string, object> output = new Dictionary<string, object>();
            Exception error = null;

            // register events
            app.OnUnhandledException = (a) =>
            {
                _log.Error("Workflow failed: #" + a.InstanceId, a.UnhandledException);

                return UnhandledExceptionAction.Terminate;
            };

            app.Aborted = (a) =>
            {
                if (_log.IsWarnEnabled)
                    _log.Warn("Workflow aborted: " + a.InstanceId, a.Reason);
            };

            app.Completed = (a) =>
            {
                output = a.Outputs;
                error = a.TerminationException;

                if (_log.IsInfoEnabled)
                    _log.Info("Workflow completed: " + a.InstanceId + " (" + a.CompletionState + ")", a.TerminationException);

                wait.Set();
            };

            app.Idle = (a) =>
            {
                if (_log.IsDebugEnabled)
                    _log.Debug("Workflow became idle (simple): " + a.InstanceId);
            };

            app.PersistableIdle = (a) =>
            {
                if (_log.IsDebugEnabled)
                    _log.Debug("Workflow became idle (persistable): " + a.InstanceId);

                return PersistableIdleAction.None;
            };

            app.Unloaded = (a) =>
            {
                if (_log.IsInfoEnabled)
                    _log.Info("Workflow unloaded: " + a.InstanceId);
            };

            // run
            app.Run(Properties.Settings.Default.MaxWorkflowTimeout);

            if (_log.IsInfoEnabled)
                _log.Info("Started workflow: " + app.Id);

            wait.WaitOne();

            // finalize
            if (error != null)
                throw new Exception("Workflow failed: " + error.Message, error);

            return output;
        }
    }
}
